package cz.swosh.stomp.web;

import org.apache.openejb.assembler.classic.EnterpriseBeanInfo;
import org.apache.openejb.jee.EjbRef;
import org.apache.openejb.jee.JaxbJavaee;
import org.apache.openejb.jee.WebApp;
import org.mortbay.jetty.Server;
import org.mortbay.jetty.webapp.WebAppContext;

import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.LinkRef;
import javax.naming.NamingException;
import java.io.FileInputStream;
import java.io.File;
import java.util.Collection;
import java.util.Properties;
import java.util.Arrays;
import org.mortbay.jetty.servlet.SessionHandler;
import org.mortbay.xml.XmlConfiguration;

/**
 * EmbeddedServer je vnořený jetty server sloužící k testování. Zvládá i injekci
 * EJB. Při debugování dochází k problému z JNI. Řešením je spoštět aplikaci s
 * s jvm argumentem com.sun.xml.bind.v2.bytecode.ClassTailor.noOptimize=true.
 * V maven projektu netbeans se v properties projektu v katgoriích, v položkách action
 * přidá argument jako property.
 *  
 *
 * @author upravil swosh, převzato z http://openejb.apache.org/functional-testing-with-openejb-jetty-and-selenium.html
 */
public class EmbeddedServer {

    private static EmbeddedServer instance = new EmbeddedServer();
    private Server server;
    private InitialContext ic;
    WebAppContext context;

    /**
     * V konstruktoru se vytvoří kontext pro testovací server.
     * Obsahuje definici datasourcu a portu, na kterém server bude běžet.
     */
    private EmbeddedServer() {
        try {
            Properties p = new Properties();
            p.setProperty(Context.INITIAL_CONTEXT_FACTORY,
                    "org.apache.openejb.client.LocalInitialContextFactory");
            /*http://openejb.979440.n4.nabble.com/PersistenceContext-problems-after-upgrade-to-3-1-td980964.html
            openejb.deployments.classpath.ear je normalně na hodnotě true a to pak způsobuje více násobné
            načtení persistence unit. Existuje i jiné řešení problému. Toto je nejsnažší
             */
            /*Postgre nastavení
             * Je třebe ještě přidat driver do pom
            p.put("openejb.deployments.classpath.ear", "false");
            
            p.put("openejb.deployments.classpath.ear", "false");
            p.put("stomp", "new://Resource?type=DataSource");
            p.put("stomp.eclipselink.ddl-generation", "drop-and-create-tables");
            //p.put("stomp.JtaManaged", "false");
            
            p.put("stomp.JdbcDriver", "org.postgresql.Driver");
            p.put("stomp.JdbcUrl", "jdbc:postgresql://localhost:5432/stomptest");
            
            p.put("stomp.username", "stomp");
            p.put("stomp.password", "stomp");
            
            //p.put("stomp.eclipselink.ddl-generation", "drop-and-create-tables");
             */
            /*
             * Nastavení persistence
             */
            p.put("openejb.deployments.classpath.ear", "false");
            p.put("stomp", "new://Resource?type=DataSource");
            //p.put("stomp.eclipselink.ddl-generation", "drop-and-create-tables");
            p.put("eclipselink.ddl-generation", "create-tables");
            //p.put("stomp.JtaManaged", "false");

            p.put("stomp.JdbcDriver", "org.apache.derby.jdbc.ClientDriver");
            p.put("stomp.JdbcUrl", "jdbc:derby://localhost:1527/sun-appserv-samples;create=true");

            p.put("stomp.username", "APP");
            p.put("stomp.password", "APP");
           /*
             * p.put("stompmail", "new://Resource?type=javax.mail.Session");
            p.put("stompmail.mail.smtp.host", "smtp.gmail.com");
           
            p.put("stomp_mail.mail.transport.protocol", "smtps");
            p.put("stomp_mail.mail.smtp.auth", "true");
            p.put("stomp_mail.mail.smtp.port", "465");
            p.put("stomp_mail.mail.smtp.user", "stompear@gmail.com");
            p.put("stomp_mail.password", "superheslo");
            p.put("stomp_mail.mail.smtp.from", "stompear@gmail.com");
            p.put("stomp_mail.mail.smtp.debug", "true");
            p.put("stomp_mail.mail.smtp.socketFactory.port", "465");
            p.put("stomp_mail.mail.smtp.quitwait", "false");
            p.put("stomp_mail.mail.smtp.starttls.enable", "true");
            p.put("stomp_mail.mail.smtp.socketFactory.class", "javax.net.ssl.SSLSocketFactory");
            */
            ic = new InitialContext(p);
            WebAppContext context = new WebAppContext();
            context.setWar(computeWarPath());
            InitialContext initialContext = setupJndi(context);
            context.setSessionHandler(new SessionHandler(new MySessionManager()));
            context.setServletHandler(new EmbeddedServerServletHandler(initialContext));
            context.setContextPath("/stompear-web");
            server = new Server(9091);
            //server = new Server(9091);
            server.addHandler(context);

            server.start();

            boolean running = server.isRunning();
            if (running) {
                System.out.println("Server běží.");
            } else {
                throw new Exception("Server neběží");
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * Nastaví kontext s ejb odkazy (cestami v jndi k nim). Ty se dají
     * použít k vlastní implementaci injektáže bean.
     * @param context z třídy WebApp
     * @return context pro openejb
     * @throws NamingException
     */
    private InitialContext setupJndi(WebAppContext context) throws NamingException {

        InitialContext initialContext = new InitialContext();
        WebApp webApp = getWebApp(context);
        Collection<EjbRef> refs = webApp.getEjbRef();
        for (EjbRef ref : refs) {
            String ejbLink = ref.getEjbLink();

            EnterpriseBeanInfo beanInfo = new EJBHelper().getEJBInfo(ejbLink);
            if (beanInfo.jndiNames != null && beanInfo.jndiNames.size() > 0) {
                String jndiName = "java:openejb/ejb/" + beanInfo.jndiNames.get(0);
                initialContext.bind("java:comp/env/" + ref.getEjbRefName(), new LinkRef(jndiName));
            }
        }
        return initialContext;
    }

    /**
     * Zjišťuje cestu k waru
     * @return cesta k waru
     */
    private String computeWarPath() {
        String currentPath = new File(".").getAbsolutePath();
        String warPath;
//Pro win i linux rozdělí cestu na jednotilivé adresáře
        String[] pathParts = currentPath.split("(\\\\|/)+");
//Najde adresář web části aplikace
        int webPart = Arrays.asList(pathParts).indexOf("stompear-web");
        if (webPart == -1) {
            warPath = "stompear-web/src/main/webapp";
        } else {
            StringBuffer buffer = new StringBuffer();

            for (int i = 0; i < webPart; i++) {
                buffer.append(pathParts[i]);
                buffer.append(File.separator);
            }

            buffer.append("stompear-web/src/main/webapp");
            warPath = buffer.toString();
        }
        return warPath;
    }

    public static EmbeddedServer getInstance() {
        return instance;
    }

    public Server getServer() {
        return server;
    }

    public InitialContext getInitialContext() {
        return ic;
    }

    public static void main(String[] args) {
        try {
            EmbeddedServer.getInstance().getServer().join();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * Převedený web.xml pro jettyho.
     * @param context WebAppContext
     * @return
     */
    private WebApp getWebApp(WebAppContext context) {
        WebApp webApp = null;

        try {
            FileInputStream is = new FileInputStream(new File(context.getWar() + "/WEB-INF/web.xml").getAbsolutePath());
            webApp = (WebApp) JaxbJavaee.unmarshal(WebApp.class, is);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return webApp;
    }

    public WebAppContext getWebAppContext() {
        return context;
    }
}
